using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

using MJB.UI.ContentEditor.Document.Version1;

namespace MJB.UI.ContentEditor.Document {
	
	/// <summary>
	/// This class returns an IDocumentReader based on the pre-processor's Version
	/// value, using a different compiled schema version.
	/// </summary>
	public sealed class DocumentReaderFactory {

		/// <summary>
		/// This dictionary is used to look up a schema reader based on the version namespace string.
		/// </summary>
		private static Dictionary<string, Type> mVersionLookup;

		/// <summary>
		/// The static constructor uses reflection to look up all of the schema readers in this 
		/// assembly.
		/// </summary>
		static DocumentReaderFactory() {

			mVersionLookup = new Dictionary<string, Type>();

			Type []types = Assembly.GetExecutingAssembly().GetTypes();
			foreach(Type type in types) {

				if(type.IsClass) {

					object []attributes = type.GetCustomAttributes(true);
					foreach(object attribute in attributes) {

						if(attribute.GetType() == typeof(DocumentReaderHandlerAttribute)) {

							// Add this version to the version lookup:
							DocumentReaderHandlerAttribute schemaAttribute = (DocumentReaderHandlerAttribute)attribute;
							mVersionLookup.Add(schemaAttribute.VersionNamespace, type);
							break;

						}

					}

				}

			}

		}

		/// <summary>
		/// Nothing to do.
		/// </summary>
		private DocumentReaderFactory() {
		}

		/// <summary>
		/// Returns a new instance of an IDocumentReader object that can service the latest 
		/// document version.
		/// </summary>
		/// <returns></returns>
		public static IDocumentReader GetLatestReader() {
			return (IDocumentReader)Activator.CreateInstance(mVersionLookup[DocumentConstants.LATEST_VERSION_NAMESPACE]);
		}

		/// <summary>
		/// Reads the input stream, determines what version it is, and returns 
		/// an instance of an IDocumentReader that can service that version of the document.
		/// </summary>
		/// <param name="reader"></param>
		/// <returns></returns>
		public static IDocumentReader GetReader(Stream stream) {

			IDocumentReader documentReader = null;
			XmlReader xmlReader = null;

			// We have to read two times: one time to find the type of file that this
			// is, and a second time to deserialize it:
			try {

				// Create the text reader:
				xmlReader = new XmlTextReader(stream);

				// Read the namespace:
				while(xmlReader.Read()) {

					// Get the namespace from the root element:
					if(xmlReader.NodeType == XmlNodeType.Element) {

						if(!mVersionLookup.ContainsKey(xmlReader.NamespaceURI))
							throw new Exception("Unknown namespace version '" + xmlReader.NamespaceURI + "'.");

						// Create the reader:
						Type readerType = mVersionLookup[xmlReader.NamespaceURI];
						documentReader = (IDocumentReader)Activator.CreateInstance(readerType);
						break;

					}

				}

				// Barf if we can't find a handler for this version.
				if(documentReader == null)
					throw new Exception("Could not find a document reader that handles this version of the protocol.");

			} finally {

				if(xmlReader != null)
					xmlReader.Close();

			}

			return documentReader;

		}

	}

}
